package com.hnxxxy.common.shiro;

import java.util.*;

import com.hnxxxy.common.shiro.filter.CheckSessionControlFilter;
import com.hnxxxy.common.shiro.filter.JwtFilter;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;

/**
 * @Description:
 * @Author: SUHUI
 * @EMAIL:13319575368@163.COM
 * @Date: 2020/4/22 12:36
 * @ACTION:shiro配置类
 * @return: null
 **/
@Configuration
public class ShiroConfig {
    /**
     * @Description:shiro securityManager
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm,
                                                                  @Qualifier("jwtRealm")JwtRealm jwtRealm,
                                                                  @Qualifier("MySessionManager") SessionManager sessionManager,
                                                                  @Qualifier("MyCacheManager") CacheManager cacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(authenticator());
        //关联realm
        List<Realm> realms = new ArrayList<Realm>(2);
        realms.add(userRealm);
        realms.add(jwtRealm);
        securityManager.setRealms(realms);
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager);
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }
    /**
     * @Description:自定义shiro过滤器
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager,
                                                            @Qualifier("checkSessionControlFilter") AccessControlFilter accessControlFilter){

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> map=new HashMap<>();
        map.put("checkLogin",accessControlFilter);
        map.put("jwtFilter",getJwtFilter());
        shiroFilterFactoryBean.setFilters(map);
        /**
         * Shiro内置过滤器，可以实现权限相关的拦截器
         *    常用的过滤器：
         *       anon: 无需认证（登录）可以访问
         *       authc: 必须认证才可以访问
         *       user: 如果使用rememberMe的功能可以直接访问 user[]
         *       perms： 该资源必须得到资源权限才可以访问 perms[user:add]
         *       role: 该资源必须得到角色权限才可以访问
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        filterMap.put("/","anon");
        filterMap.put("/_view/**","anon");
        filterMap.put("/js/**","anon");
        filterMap.put("/common/**","anon");
        filterMap.put("/css/**","anon");
        filterMap.put("/images/**","anon");
        filterMap.put("/plugins/**","anon");
        filterMap.put("/sys/toLogin", "anon");
        filterMap.put("/jf/**", "anon");
        filterMap.put("/total/**", "anon");
/*
        filterMap.put("/sys/loginOut", "logout");
*/
        filterMap.put("/service/**","jwtFilter");
        filterMap.put("/**", "authc,checkLogin");
        //修改调整的登录页面
        shiroFilterFactoryBean.setLoginUrl("/");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }


    @Bean("checkSessionControlFilter")
    public CheckSessionControlFilter checkSessionControlFilter(@Qualifier("redisTemplate1")RedisTemplate redisTemplate,
                                                      @Qualifier("MySessionManager") SessionManager sessionManager){
        CheckSessionControlFilter checkSessionControlFilter=new CheckSessionControlFilter();
        checkSessionControlFilter.setRedisTemplate(redisTemplate);
        checkSessionControlFilter.setSessionManager(sessionManager);
        return checkSessionControlFilter;
    }
    /**
     * 不向 Spring容器中注册 JwtFilter Bean，防止 Spring 将 JwtFilter 注册为全局过滤器
     * 全局过滤器会对所有请求进行拦截，而本例中只需要拦截除 /login 和 /logout 外的请求
     * 另一种简单做法是：直接去掉 jwtFilter()上的 @Bean 注解
     */
    @Bean
    public FilterRegistrationBean<Filter> registration(CheckSessionControlFilter filter) {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>(filter);
        registration.setEnabled(false);
        return registration;
    }
   /* @Bean("cstJwtFilter")*/
    public JwtFilter getJwtFilter(){
        return new JwtFilter();
    }
    /**
     * @Description:自定义Realm
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean(name="userRealm")
    public UserRealm getRealm(@Qualifier("hashMatch")HashedCredentialsMatcher hashedCredentialsMatcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return userRealm;
    }

    @Bean(name="jwtRealm")
    public JwtRealm JwtRealm(){
        JwtRealm jwtRealm = new JwtRealm();
        CredentialsMatcher credentialsMatcher = new JwtCredentialsMatcher();
        jwtRealm.setCredentialsMatcher(credentialsMatcher);
        return jwtRealm;
    }



    /**
     * @Description:开启前端thymeleaf中shiro标签使用
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }


    /**
     * @Description:开启shiro注解支持（AOP）
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * @Description:开启shiro注解支持
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


    /**
     * @Description:自定义适配器(密码,登录错误限制)
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean("hashMatch")
    public HashedCredentialsMatcher hashedCredentialsMatcher(@Qualifier("redisTemplate1")RedisTemplate redisTemplate){
        ShiroMatcher credentialsMatcher = new ShiroMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1000);
        //此处的设置，true加密用的hex编码，false用的base64编码
        credentialsMatcher.setStoredCredentialsHexEncoded(false);
        credentialsMatcher.setRedisTemplate(redisTemplate);
        return credentialsMatcher;
    }

    /**
     * @Description:配置cacheManager
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean(value = "MyCacheManager")
    public CacheManager cacheManager(@Qualifier("redisTemplate1")RedisTemplate redisTemplate) {
        MyShiroCacheManager myShiroCacheManager=new MyShiroCacheManager();
        myShiroCacheManager.setTemplateRedis(redisTemplate);
        return myShiroCacheManager;
    }

    /**
     * @Description:配置sessionDao
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean(value = "MySessionDao")
    public SessionDAO redisSessionDAO(@Qualifier("redisTemplate1")RedisTemplate redisTemplate) {
        ShrioRedisSesssionDao redisSessionDAO = new ShrioRedisSesssionDao();
        redisSessionDAO.setRedisTemplate(redisTemplate);
        return redisSessionDAO;
    }

    /**
     * @Description:配置sessionManager
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean(value = "MySessionManager")
    public DefaultWebSessionManager SessionManager(@Qualifier("MySessionDao")SessionDAO sessionDAO) {
        ShiroSessionManager sessionManager = new ShiroSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    /**
     * Shiro生命周期处理器
     *
     */
    @Bean
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * @Description: 配置 ModularRealmAuthenticator 解决多reaml下异常输出问题
     * @Author: SUHUI
     * @EMAIL:13319575368@163.COM
     * @Date: 2020/5/21 22:27
     **/
    @Bean
    public ModularRealmAuthenticator authenticator() {
        ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator();
        // 设置多 Realm的认证策略，默认 AtLeastOneSuccessfulStrategy
        AuthenticationStrategy strategy = new FirstSuccessfulStrategy();
        authenticator.setAuthenticationStrategy(strategy);
        return authenticator;
    }
}